package com.ayitubie.refuelweb.service;

import com.ayitubie.refuelweb.config.interceptor.loginUserInterceptor.UserContext;
import com.ayitubie.refuelweb.config.jedis.JedisService;
import com.ayitubie.refuelweb.config.lua.RedisLuaUtil;
import com.ayitubie.refuelweb.elasticsearch.LogRepository;
import com.ayitubie.refuelweb.entity.GasStation;
import com.ayitubie.refuelweb.entity.LogEsVo;
import com.ayitubie.refuelweb.entity.RefuelLog;
import com.ayitubie.refuelweb.entity.RefuelLogForExcel;
import com.ayitubie.refuelweb.mapper.RefuelLogMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Slf4j
@Configuration
@Service
public class RefuelLogServ {

    @Autowired
    JedisService jedisService;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    private RefuelLogMapper refuelLogMapper;

    @Autowired
    private GasStationServ gasStationServ;

    @Autowired
    private LogRepository logRepository;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    RedisLuaUtil redisLuaUtil;

    @Value("${sftp.host}")
    private String uploadHost;

    @Value("${sftp.downloadPort}")
    private String downloadPort;


    public PageInfo<RefuelLog> getRefuelLogByUserId(Integer pageNum, Integer pageSize, float payMoney, float payMoneyLow, List stationIds) {
        PageHelper.startPage(pageNum, pageSize);
        List<RefuelLog> list = refuelLogMapper.getRefuelLogByUserId(UserContext.getUser().getUserId(), payMoney, payMoneyLow, stationIds);
        for (RefuelLog log : list) {
            if (StringUtils.isNotEmpty(log.getPhoto())) {
                log.setImageUrl("http://" + uploadHost + ":" + downloadPort + "/refuel/log/picture/" + log.getPhoto());
            }
        }
        PageInfo pageInfo = new PageInfo(list);
        return pageInfo;
    }

    public List<RefuelLog> getPerMonth() {
        List<RefuelLog> list = refuelLogMapper.getPerMonth(UserContext.getUser().getUserId());
        System.out.println(list);
        return list;
    }

    public List<RefuelLogForExcel> getRefuelLogsAll() {
        List arr = new ArrayList();
        List<RefuelLogForExcel> list = refuelLogMapper.getRefuelLogByUserId(UserContext.getUser().getUserId(), (float) 0, (float) 0, arr);
        return list;
    }

    public void processExcel() throws InterruptedException {
        String key = UUID.randomUUID().toString(); //redis key
        // 总数
        try {
            int count = refuelLogMapper.countRefuellog(UserContext.getUser().getUserId());
            // 每页2条
            int pageSize = 2;
            // 总页数
            int pageCount = (int) Math.ceil(count / pageSize);
            exportProgress(key, pageCount, 0);
            for (int currentPage = 1; currentPage <= pageCount; currentPage++) {
                // 分页查询
                refuelLogMapper.getRefuelLogByUserIdWithLimit(UserContext.getUser().getUserId(), (currentPage - 1) * pageSize, pageCount);
                // 处理到第几页
                exportProgress(key, pageCount, currentPage);
                Thread.sleep(200);
            }
            jedisService.expire(key,3600);
        } catch (InterruptedException e) {
            jedisService.del(key);
            throw new RuntimeException(e);
        }
    }

    /**
     * 进度
     */
    private void exportProgress(String exportId, int totalPages, int currentPage) {
        String progress = "{exportId:" + exportId + ",totalPages:" + totalPages + "," + "currentPage:" + currentPage + "}";
        jedisService.set(exportId, progress);
    }

    public Boolean checkToken(HttpServletRequest request) throws Exception {
        String token = request.getHeader("antiWeightToken");

        if (StringUtils.isBlank(token)) {
            token = request.getParameter("antiWeightToken");
            if (StringUtils.isBlank(token)) {
                throw new Exception("未获取防重令牌！");
            }
        }
        List<String> keyList = new ArrayList();
        keyList.add(token);
        String res = redisLuaUtil.runLuaScript("checkidem.lua", keyList);

        if (res.equals("1")) {
            return true;
        } else {
            throw new Exception("重复提交！");
        }
    }


    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public Long refuelCreate(RefuelLog refuelLog, HttpServletRequest request) throws Exception {
        //放重校验
        this.checkToken(request);

        // 设置上一次公里数，为了计算平均油耗
        int pre_mileage = refuelLogMapper.getLastRefuel(UserContext.getUser().getUserId());
        refuelLog.setPre_mileage(pre_mileage);

        // 插入数据库
        refuelLogMapper.refuelCreate(refuelLog);

        // 保存完就释放一个信号量
        RSemaphore semaphore = redissonClient.getSemaphore("TimeLimit");
        semaphore.release();

        // 插入elasticsearch
        GasStation gs = gasStationServ.getSation(refuelLog.getRefuel_station_id());
        refuelLog.setStation_name(gs.getStationName());
        refuelLog.setStation_address(gs.getStationAddress());
        logRepository.save(refuelLog);

        return refuelLog.getRefuel_id();
    }

    public int delRefuelById(Long refuel_id) {
        logRepository.deleteById(refuel_id);
        return refuelLogMapper.delRefuelById(refuel_id);
    }

    public float getAvgOil() {
        return refuelLogMapper.getAvgOil(UserContext.getUser().getUserId());

    }

    public Long updateRefuelById(RefuelLog refuelLog) {
        GasStation gs = gasStationServ.getSation(refuelLog.getRefuel_station_id());
        refuelLog.setStation_name(gs.getStationName());
        refuelLog.setStation_address(gs.getStationAddress());
        refuelLogMapper.updateRefuelById(refuelLog);
        logRepository.save(refuelLog);
        return refuelLog.getRefuel_id();
    }

    /**
     * 爬油价
     *
     * @return
     */
    public String getOilPrice(Boolean clearCache) {
        RLock lock = redissonClient.getLock("oilPrice-lock");
        lock.lock();
        if (clearCache && jedisService.exists("oilPrice")) {
            jedisService.del("oilPrice");
        }
        String str = null;
        try {
            //Thread.sleep(10*1000);
            // 先从redis读
            String oilPrice = jedisService.get("oilPrice");
            if (StringUtils.isNotEmpty(oilPrice) && !"{}".equals(oilPrice)) {
                log.info("油价：从redis读 [{}]", oilPrice);
                str = oilPrice;
                // 读不到，直接爬虫
            } else {

                ObjectMapper objectMapper = new ObjectMapper();
                LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
                String url = "http://www.qiyoujiage.com/beijing.shtml";
                Document doc = Jsoup.connect(url).get();
                Elements ele = doc.select("div#youjia>dl");

                for (Element tr : ele) {
                    Elements dt = tr.select("dt");
                    Elements dd = tr.select("dd");
                    map.put(dt.first().html().substring(2), dd.last().html());
                }
                doc = null;
                ele = null;
                str = objectMapper.writeValueAsString(map);
                jedisService.set("oilPrice", str);
                log.info("油价：从爬虫读 [{}]", str);
                jedisService.expire("oilPrice", 8 * 60 * 60);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return str;
    }

    public static void main(String[] args) {
        try {
            List<String> list = new ArrayList<String>();
            String url = "https://tianqi.moji.com/weather/china/beijing/xicheng-district";
            Document doc = Jsoup.connect(url).get();
            Elements today2 = doc.select("div.forecast ul.days");
            Integer i = 0;
            for (Element text : today2) {
                String temp = "";
                Elements day = text.select(">li");
                int j = 0;
                for (Element text1 : day) {
                    if (j != 0) {
                        temp += text1.text() + " ";
                    }
                    j++;
                }
                i++;
                list.add(temp);
            }
            System.out.println(list);

            LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
            url = "http://www.qiyoujiage.com/beijing.shtml";
            Document doc1 = Jsoup.connect(url).get();
            Elements ele1 = doc1.select("div#youjia>dl");

            for (Element tr : ele1) {
                Elements dt = tr.select("dt");
                Elements dd = tr.select("dd");
                map.put(dt.first().html().substring(2), dd.last().html());
            }
            System.out.println(map.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }

    }

    /**
     * 爬天气
     *
     * @return
     */
    public String getWeather(Boolean clearCache) {
        RLock lock = redissonClient.getLock("weather-lock");
        lock.lock();
        if (clearCache && jedisService.exists("weather")) {
            jedisService.del("weather");
        }
        String result = "";
        List<String> list = new ArrayList<String>();
        try {
            String url = "https://tianqi.moji.com/weather/china/beijing/xicheng-district";
            // 先从redis读
            String weather = jedisService.get("weather");
            if (StringUtils.isNotEmpty(weather) && !"[]".equals(weather)) {
                result = weather;
                log.info("天气：从redis读 [{}]", result);
                // 读不到，直接爬虫
            } else {
                ObjectMapper objectMapper = new ObjectMapper();
                Document doc = Jsoup.connect(url).get();
                Elements threeDays = doc.select("div.forecast ul.days");
                for (Element text : threeDays) {
                    String temp = "";
                    Elements day = text.select(">li");
                    int j = 0;
                    for (Element tday : day) {
                        if (j != 0) {
                            temp += tday.text() + " ";
                        }
                        j++;
                    }
                    list.add(temp);
                }
                doc = null;
                threeDays = null;
                result = objectMapper.writeValueAsString(list);
                jedisService.set("weather", result);
                log.info("天气：从爬虫读 [{}]", result);
                jedisService.expire("weather", 8 * 60 * 60);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return result;
    }

    /**
     * es高亮
     *
     * @param esVo
     * @return
     */
    public List<SearchHit<RefuelLog>> elasticsearchLog(LogEsVo esVo) {
        //Iterable<RefuelLog> items = logRepository.findAll(Sort.by("refuel_time").descending());

        //自定义查询器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("station_name")
                .preTags("<b class='hitTarget'>")
                .postTags("</b>")
                .fragmentSize(1000)
                .numOfFragments(0);
        nativeSearchQueryBuilder.withQuery(
                        QueryBuilders.boolQuery()
                                .must(QueryBuilders.matchQuery("station_name", esVo.getStationName()))
                                .must(QueryBuilders.termQuery("oil_type", esVo.getOilType()))
                                .must(QueryBuilders.termQuery("pay_type", esVo.getPayType()))
                ).withFilter(QueryBuilders.rangeQuery("pay_money").gte(esVo.getPayMoneyFrom()).lte(esVo.getPayMoneyTo()))
                .withSort(SortBuilders.fieldSort("refuel_time").order(SortOrder.DESC))
                .withPageable(PageRequest.of(0, 1000))
                .withHighlightBuilder(highlightBuilder);
        SearchHits<RefuelLog> results = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), RefuelLog.class);
        for (SearchHit hit : results.getSearchHits()) {
            log.info("ES输出命中记录：[{}]", hit.getContent());
            log.info("ES输出命中高亮字段：[{}]", hit.getHighlightFields());
        }
        return results.getSearchHits();
    }
}
